package cate.game.train.hero;

import cate.common.table.d.*;
import cate.common.table.item.StarUpItemRow;
import cate.common.table.item.hero.row.HeroBaseRow;
import cate.common.table.item.hero.row.HeroGradeRow;
import cate.common.table.item.hero.row.HeroLevelRow;
import cate.common.table.item.staruptask.HeroStarTaskRow;
import cate.common.table.res.hero.back.row.*;
import cate.common.table.res.hero.inherit.row.HeroInheritCostRow;
import cate.common.table.res.hero.inherit.row.HeroInheritSelectRow;
import cate.common.table.res.hero.sacrifice.row.SacrificeGradeRow;
import cate.common.table.res.hero.sacrifice.row.SacrificeLevelRow;
import cate.common.table.res.hero.sacrifice.row.SacrificeStarRow;
import cate.common.util.GameResult;
import cate.game.GameBody;
import cate.game.client.msg.MergeMsgSender;
import cate.game.event.RoleEventPublisher;
import cate.game.event.hero.HeroLevelChangeEvent;
import cate.game.event.hero.HeroPowerChangeEvent;
import cate.game.event.hero.HeroStarChangeEvent;
import cate.game.notice.msg.NoticeDialogResp;
import cate.game.res.CostSelect;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.res.RewardShow;
import cate.game.role.Role;
import cate.game.role.bag.hero.Hero;
import cate.game.role.bag.vo.DogCostVO;
import cate.game.role.bag.vo.StarUpResVO;
import cate.game.role.res.worldtree.msg.HeroShareDataResp;
import cate.game.role.res.worldtree.msg.HeroShareDelResp;
import cate.game.train.hero.msg.*;
import cate.game.train.yoke.msg.YokeOffAllResp;
import easy.java.practice.errorcode.EcResult;
import easy.java.util.StrTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

import static cate.game.role.farm.herotrain.TrainHeroFight.csContainsOtherOption;

/**
 * 英雄培养处理器
 */
@Slf4j
public class TrainHeroHandler implements RoleEventPublisher {

  public GameResult<Hero> findHeroResult(Role role, String uid) {
    GameResult<Hero> r = new GameResult<>();
    Hero hero = role.getBag().hero.getItem(uid);
    if (hero == null) {
      return r.fail("英雄不存在！");
    }
    r.data = hero;
    return r.success();
  }

  public EcResult<Void> lockHero(Role role, String uid) {
    EcResult<Void> r = new EcResult<>();
    EcResult<Hero> findRes = findHeroResult(role, uid);
    if (!findRes.ok()) {
      return r.fail(findRes.message);
    }
    Hero hero = findRes.data;
    hero.lock.manual = !hero.lock.manual;
    role.sendNow(new HeroLockResp(hero.lock));
    return r;
  }

  /**
   * 升级指定英雄
   */
  public EcResult<Hero> levelUp(Role role, String uid) {
    EcResult<Hero> r = findHeroResult(role, uid);
    if (!r.ok()) {
      return r;
    }
    Hero hero = r.data;
    // 记录下升级前的等级
    int oldLevel = hero.level;
    HeroBaseRow hbt = role.getGame().table.hero.base.get(hero.tid);
    String msg = "英雄升级成功";
    int curTargetLevel = oldLevel + 1;
    int targetLevel = oldLevel + 5;
    MergeMsgSender sender = new MergeMsgSender();

    int levelLimit = hbt.levelMax;
    for (Integer taskId : hero.starTaskList) {
      HeroStarTaskRow taskRow = role.getGame().table.starUpTask.task.get(taskId);
      if (taskRow == null) {
        msg = "数据错误";
        return r.fail(msg);
      }
      levelLimit += taskRow.levelBreakValue;
    }

    while (curTargetLevel <= targetLevel) {
      HeroLevelRow tpl = role.getGame().table.hero.level.getByLevel(curTargetLevel);
      if (tpl == null) {
        msg = "该英雄已升到顶级";
        break;
      }
      if (hero.shareLevel > 0) {
        msg = "该英雄在世界之树中, 无法升级";
        break;
      }
      if (hero.level >= GDHero.HERO_WORLD_TREE_LEVEL_LIMIT && curTargetLevel > role.getRes().worldTree.heroShare.shareLvl + GDHero.HERO_WORLD_TREE_LEVEL_BETWEEN) {
        msg = StrTool.format("英雄与世界之树等级差不能大于{}级", GDHero.HERO_WORLD_TREE_LEVEL_BETWEEN);
        break;
      }
      if (hero.grade < tpl.gradeRequire) {
        msg = StrTool.format("英雄阶数不足,需要{}阶", tpl.gradeRequire);
        break;
      }
      if (hbt.star < tpl.starRequire) {
        msg = "英雄星级不足";
        break;
      }

      if (levelLimit < curTargetLevel) {
        msg = "当前等级已达英雄当前状态上限";
        break;
      }
      GameResult<MixResItem> consumeRes = new MixRes(tpl.consumeStr).consume(role);
      if (!consumeRes.ok()) {
        if (curTargetLevel == oldLevel + 1) {
          role.getGame().notice.tipResItem(role, consumeRes.data);
        }
        msg = consumeRes.message;
        break;
      }
      hero.level++;
      curTargetLevel++;
    }
    //如果一级都没有升
    if (curTargetLevel == oldLevel + 1) {
      return r.fail(msg);
    }
    sender.send(role, true);
    publishEvent(role, new HeroLevelChangeEvent(role, hero, hero.level));
    publishEvent(role, new HeroPowerChangeEvent(role, hero));
    role.getBase().updatePower();

    // 判断升级后的英雄等级所在的等级区间有没有变化
    int oldInterval = parseInterval(oldLevel);
    int newInterval = parseInterval(hero.level);
    if (newInterval > oldInterval) {
      // 发生变化后记录
      role.getHistory().action.heroLevelNumTotal(hero.level);
      role.getHistory().action.heroLevelNumUpdate();
    }
    role.getHistory().action.heroLevelUp();
    role.getHistory().action.heroLevelUpSum(hero.level - oldLevel);
    role.getBag().hero.markHeroChanged();
    return r.success();
  }

  /**
   * 获取英雄等级所在的区间
   */
  private int parseInterval(int newLevel) {
    int interval = 0;
    for (int i : GDHistory.HERO_LEVEL_NUM_OPT) {
      if (newLevel >= i && i >= interval) {
        interval = i;
      }
    }
    return interval;
  }

  public void batchStarUp(Role role, List<DogCostVO> list) {
    if (CollectionUtils.isEmpty(list)) {
      role.getGame().notice.message(role, "未选中要吞噬的英雄");
      return;
    }
    RewardShow rewardShow = new RewardShow();
    List<StarUpResVO> res = new ArrayList<>();
    for (DogCostVO vo : list) {
      EcResult<Hero> r = findHeroResult(role, vo.uid);
      if (!r.ok()) {
        role.getGame().notice.message(role, r.message);
        continue;
      }
      Hero old = r.data;
      int tidOld = old.tid;
      r = normalStarUp(role, old, vo.csbbMap, rewardShow);
      if (r.ok()) {
        StarUpResVO resVo = new StarUpResVO();
        resVo.uid = vo.uid;
        resVo.tidOld = tidOld;
        resVo.tidNew = r.data.tid;
        res.add(resVo);
        HeroBaseUpdateResp.send(role, r.data);
      } else {
        role.getGame().notice.message(role, r.message);
      }
    }
    if (!res.isEmpty()) {
      role.getBase().updatePower();
      role.sendNow(new HeroBatchStarUpResp(res, rewardShow));
    }
    role.getBag().hero.markHeroChanged();
  }
//
//    /**
//     * 英雄升星
//     */
//    private GameResult<Hero> normalStarUp(Role role, Hero hero, CostSelect cs, MixRes dogCost, RewardShow rewardShow) {
//        GameResult<Hero> r = new GameResult<>();
//        HeroBaseRow tpl = role.getGame().table.hero.base.get(hero.tid);
//        if (tpl.upType != GDHero.UpType.NORMAL) {
//            return r.fail("存在不支持自动升星的英雄");
//        }
//        if (tpl.nextStarId <= 0) {
//            return r.fail("食灵已升到顶级");
//        }
//
//        MixRes starCost = new MixRes(tpl.cost);
//        // 升星道具检测
//        if (dogCost != null && !dogCost.isEmpty()) {
//            for (MixResItem dogItem : dogCost.items) {
//                EcResult<Void> rebuildRes = rebuildStarCost(role, starCost, dogItem);
//                if (!rebuildRes.ok()) {
//                    return r.fail(rebuildRes.message);
//                }
//            }
//            EcResult<MixResItem> dogCostRes = dogCost.consumeCheck(role, null);
//            if (!dogCostRes.ok()) {
//                return r.fail(dogCostRes.message);
//            }
//        }
//        // 具体的消耗
//        EcResult<MixResItem> rCost = starCost.consumeCheck(role, cs);
//        if (!rCost.ok()) {
//            role.getGame().notice.tipResItem(role, rCost.data);
//            return r.fail(rCost.message);
//        }
//        // 一定要返回在先，否则会因为英雄被消耗而找不到
//        EcResult<RewardShow> rReturn = forHeroList(role, cs, starUpOpt());
//        if (!rReturn.ok()) {
//            return r.fail(rReturn.message);
//        }
//        if (rReturn.data != null && !rReturn.data.isEmpty()) {
//            rewardShow.merge(rReturn.data);
//        }
//        MergeMsgSender sender = new MergeMsgSender();
//        starCost.consumeDirect(role, cs, sender);
//        if (dogCost != null) {
//            dogCost.consumeDirect(role, null);
//        }
//        sender.send(role, true);
//
//        hero.tid = tpl.nextStarId;
//        if (hero.lock.heroReplace) {
//            hero.lock.releaseReplaceLock(role);
//        }
//        HeroBaseRow newHeroRow = role.getGame().table.hero.base.get(hero.tid);
//        //先触发事件
//        publishEvent(role, new HeroStarChangeEvent(role, hero.uid, newHeroRow.star, tpl.star, tpl.camp));
//        publishEvent(role, new HeroPowerChangeEvent(role, hero));
//        // 历史记录
//        role.getHistory().action.heroStarUp(hero, newHeroRow.star);
//        role.getHistory().action.heroOccupationCollection(role, hero.getTpl(role.toPlayBuildContext()).job, newHeroRow.star);
//        role.getHistory().action.restrictHeroStarUpTo(hero.tid, 1);
//        r.data = hero;
//        role.getBag().hero.markHeroChanged();
//        return r.success();
//    }
//

  public GameResult<RewardShow> checkAndConsume(Role role, Map<Integer, CostSelect> csbbMap, String costHeroStr, String costItemStr) {
    //新升星道具检测
    //1.检测选择道具
    GameResult<RewardShow> r = new GameResult<>();
    if (!role.getFarm().trainHeroFight.costSelectCheck(csbbMap, costHeroStr)) {
      return r.fail("材料选择错误");
    }
    CostSelect csWithoutPileProp = new CostSelect();
    csWithoutPileProp.options = new LinkedList<>();
    CostSelect cs = new CostSelect();
    cs.options = new LinkedList<>();
    for (CostSelect singleCs : csbbMap.values()) {
      for (cate.game.res.Option option : singleCs.options) {
        if (option.type == GDObj.Type.HERO) {
          for (String uid : option.uidList) {
            GameResult<Hero> hero_r = this.findHeroResult(role, uid);
            if (hero_r.ok())
              if (hero_r.data.lock.isCustomedLock()) {
                return r.fail("存在被上阵、置换、羁绊等功能锁定的英雄");
              }
          }
        }

        if (option.type != GDObj.Type.PROP
                || role.getGame().table.item.base.get(option.tid).pileMax < 2) {
          if (!csContainsOtherOption(csWithoutPileProp.options, option)) {
            csWithoutPileProp.options.add(option);
          }
        } else {
          cs.options.add(option);
        }
      }
    }
    cs.options.addAll(csWithoutPileProp.options);
    MixRes allCost = new MixRes();
    //所有选择道具
    for (cate.game.res.Option option : cs.options) {
      allCost.addItem(new MixResItem(option.type, option.tid, option.uidList.size()));
    }
    if (StringUtils.isNotBlank(costItemStr)) {
      allCost.addList(new MixRes(costItemStr));
    }
    // 具体的消耗
    EcResult<MixResItem> rCost = allCost.consumeCheck(role, csWithoutPileProp);
    if (!rCost.ok()) {
      role.getGame().notice.tipResItem(role, rCost.data);
      return r.fail(rCost.message);
    }
    // 一定要返回在先，否则会因为英雄被消耗而找不到
    GameResult<RewardShow> rReturn = forHeroList(role, csWithoutPileProp, starUpOpt());
    if (!rReturn.ok()) {
      return r.fail(rReturn.message);
    }
    MergeMsgSender sender = new MergeMsgSender();
    allCost.consumeDirect(role, csWithoutPileProp, sender);
    sender.send(role, true);
    return rReturn;
  }

  /**
   * 英雄升星
   */
  private GameResult<Hero> normalStarUp(Role role, Hero hero, Map<Integer, CostSelect> csbbMap, RewardShow rewardShow) {
    GameResult<Hero> r = new GameResult<>();
    Hero old = hero;
    if (csbbMap == null) {
      return r.fail("未选择升星消耗");
    }
    if (hero.lock.heroReplace) {
      return r.fail("请先取消置换");
    }

    HeroBaseRow tpl = role.getGame().table.hero.base.get(hero.tid);
    if (tpl.upType != GDHero.UpType.NORMAL) {
      return r.fail("存在不支持自动升星的英雄");
    }
    if (tpl.worldLevelRequire > role.getRes().worldTree.heroShare.shareLvl) {
      return r.fail(String.format("共鸣等级达到%d级，可以继续升星", tpl.worldLevelRequire));
    }
    if (tpl.nextStarId <= 0) {
      return r.fail("英雄已升到顶级");
    }
    GameResult<RewardShow> rReturn = checkAndConsume(role, csbbMap, tpl.needSelectCost, tpl.cost3);
    if (!rReturn.ok()) {
      return r.fail(rReturn);
    }
    if (rReturn.data != null && !rReturn.data.isEmpty()) {
      rewardShow.merge(rReturn.data);
    }
    hero.tid = tpl.nextStarId;
    if (hero.lock.heroReplace) {
      hero.lock.releaseReplaceLock(role);
    }

    HeroBaseRow newHeroRow = role.getGame().table.hero.base.get(hero.tid);
    //先触发事件
    publishEvent(role, new HeroStarChangeEvent(role, hero.uid, newHeroRow.star, tpl.star, tpl.camp));
    publishEvent(role, new HeroPowerChangeEvent(role, hero));
    // 历史记录
    role.getHistory().action.heroStarUp(hero, newHeroRow.star);
    role.getHistory().action.heroOccupationCollection(role, hero.getTpl(role.toPlayBuildContext()).job, newHeroRow.star);
    role.getHistory().action.restrictHeroStarUpTo(hero.tid, 1);
    r.data = hero;
    role.getBag().hero.markHeroChanged();
    return r.success();
  }


  public GameResult<Hero> gradeUp(Role role, String uid) {
    GameResult<Hero> r = findHeroResult(role, uid);
    if (!r.ok()) {
      return r;
    }
    Hero hero = r.data;
    HeroBaseRow tpl = role.getGame().table.hero.base.get(hero.tid);
    HeroGradeRow gradeTpl = role.getGame().table.hero.grade.find(tpl.protoId, hero.grade);
    HeroGradeRow gradeNextTpl = role.getGame().table.hero.grade.find(tpl.protoId, hero.grade + 1);
    if (gradeNextTpl == null) {
      return r.fail("英雄已升至最高阶");
    }
    if (tpl.star < gradeNextTpl.starRequire) {
      return r.fail("英雄星级要达到{}星才可升阶", gradeNextTpl.starRequire);
    }
    if (hero.level < gradeTpl.levelLimit) {
      return r.fail("英雄等级要达到{}级才可升阶", gradeTpl.levelLimit);
    }
    GameResult<MixResItem> consumeRes = new MixRes(gradeNextTpl.consumeStr).consume(role);
    if (!consumeRes.ok()) {
      role.getGame().notice.tipResItem(role, consumeRes.data);
      return r.fail(consumeRes);
    }
    hero.grade++;
    publishEvent(role, new HeroPowerChangeEvent(role, hero));
    role.getBase().updatePower();
    HeroBaseUpdateResp.send(role, r.data);
    role.getHistory().action.heroGradeUp(hero.grade);
    role.getBag().hero.markHeroChanged();
    return r.success();
  }

  /**
   * 英雄重生
   */
  public void reborn(Role role, String uid) {
    EcResult<Hero> r = findHeroResult(role, uid);
    if (!r.ok()) {
      role.getGame().notice.message(role, r.message);
      return;
    }
    Hero hero = r.data;
    EcResult<Void> offRes = fastOffHero(role, uid);
    if (!offRes.ok()) {
      return;
    }
    if (hero.lock.islocked()) {
      role.getGame().notice.message(role, "该英雄已被锁定");
      return;
    }
    if (hero.level < GDHero.Reborn.LEVEL_LIMIT) {
      role.getGame().notice.message(role, "英雄" + GDHero.Reborn.LEVEL_LIMIT + "级后才可重生");
      return;
    }
    // 开服七天后要收费
    if (!role.getRes().reborn.canReborn()) {
      role.getGame().notice.message(role, "重生次数已用完");
      return;
    }
    role.getRes().reborn.reborn();
    List<Hero> list = new ArrayList<>();
    list.add(hero);
    EcResult<RewardShow> returnRes = calReturn(role, list, rebornOpt());
    hero.grade = GDHero.GRADE_MIN;
    hero.level = GDHero.LEVEL_MIN;
    publishEvent(role, new HeroLevelChangeEvent(role, hero, hero.level));
    publishEvent(role, new HeroPowerChangeEvent(role, hero));
    role.getBase().updatePower();
    HeroBaseUpdateResp.send(role, hero);
    role.sendNow(new HeroRebornResp(uid, hero));
    if (returnRes.ok()) {
      returnRes.data.addInsRwd(GDObj.Type.HERO, hero.uid);
      role.getGame().notice.dialog(role, returnRes.data);
    }
    role.getBag().hero.markHeroChanged();
  }

  public GameResult<Void> inherit(Role role, String inheritUid, Set<String> selectUids) {
    GameResult<Void> r = new GameResult<>();
    if (StringUtils.isBlank(inheritUid)) {
      return r.fail("错误的英雄id");
    }
    EcResult<Hero> sourceR = findHeroResult(role, inheritUid);
    if (!sourceR.ok()) {
      return r.fail(sourceR);
    }
    Hero left = sourceR.data;
    HeroBaseRow baseRow = left.getTpl(role.toPlayBuildContext());
    HeroInheritSelectRow selectRow = role.getGame().table.heroInherit.select.findRow(baseRow.protoId);
    if (selectRow == null) {
      return r.fail("该英雄无法被继承");
    }
    HeroInheritCostRow costRow = role.getGame().table.heroInherit.cost.findRow(baseRow.star);
    if (costRow == null) {
      return r.fail("该英雄无法被继承");
    }
    if (selectUids == null || selectUids.size() != costRow.costHeroNum || selectUids.contains(inheritUid)) {
      return r.fail("选择的英雄数量有误");
    }
    GameResult<List<Hero>> selectHeresRes = getSelectHeros(role, selectUids);
    if (!selectHeresRes.ok()) {
      return r.fail(selectHeresRes);
    }
    List<Hero> selectHeros = selectHeresRes.data;
    Hero example = selectHeros.get(0);
    HeroInheritSelectRow exampleSelectRow = role.getGame().table.heroInherit.select.findRow(example.getTpl(role.toPlayBuildContext()).protoId);
    if (exampleSelectRow == null || selectRow.group != exampleSelectRow.group) {
      return r.fail("该英雄无法被继承");
    }
    HeroBaseRow lowHeroRow = role.getGame().table.hero.base.getRow(baseRow.protoId, 5);
    if (lowHeroRow == null) {
      return r.fail("该英雄无法被继承");
    }
    HeroBaseRow niubiHeroRow = role.getGame().table.hero.base.getRow(example.getTpl(role.toPlayBuildContext()).protoId, baseRow.star);
    if (niubiHeroRow == null) {
      return r.fail("该英雄无法继承");
    }
    EcResult<Void> offRes = fastOffHero(role, inheritUid);
    if (!offRes.ok()) {
      return r.fail(offRes);
    }
    GameResult<MixResItem> consumeRes = new MixRes(costRow.consumeStr).consume(role);
    if (!consumeRes.ok()) {
      role.getGame().notice.tipResItem(role, consumeRes.data);
      return r.fail(consumeRes);
    }
    GameResult<RewardShow> returnRes = calReturn(role, selectHeros, inheritOpt());
    if (!returnRes.ok()) {
      return r.fail(returnRes);
    }
    MixRes starTaskReward = calcCurStarTaskReward(role, left);
    Hero h = MergeMsgSender.batchSend(role, sender -> {
      role.getBag().hero.removeItem(inheritUid, sender);
      selectUids.forEach(e -> role.getBag().hero.removeItem(e, sender));
      role.getBag().hero.addItems(backHerosCalc(role, costRow, lowHeroRow.id), sender);
      Hero newHero = left.inherit(niubiHeroRow.id);
      newHero.markPowerExpire();
      newHero.initialize(role);
      role.getBag().hero.addItem(newHero, sender);
      if (!starTaskReward.isEmpty()) {
        starTaskReward.add(role, true);
      }
      return newHero;
    });
    RewardShow reward = new RewardShow();
    reward.addInsRwd(GDObj.Type.HERO, h.uid);
    reward.addTplRwd(starTaskReward);
    role.getGame().notice.dialog(role, reward);
    role.sendNow(new HeroTransferResp(left, h));
    return r;
  }

  private GameResult<List<Hero>> getSelectHeros(Role role, Set<String> selectUids) {
    GameResult<List<Hero>> r = new GameResult<>();
    List<Hero> selectHeros = new ArrayList<>();
    GameResult<Hero> findRes;
    int selectHeroId = 0;
    int selectHeroStar = 0;
    for (String heroUid : selectUids) {
      findRes = findHeroResult(role, heroUid);
      if (!findRes.ok()) {
        return r.fail("选择的英雄不存在");
      }
      if (selectHeroId == 0) {
        selectHeroId = findRes.data.tid;
        selectHeroStar = findRes.data.getTpl(role.toPlayBuildContext()).star;
      }
      if (findRes.data.tid != selectHeroId || selectHeroStar != 5) {
        return r.fail("选择的英雄有误");
      }
      selectHeros.add(findRes.data);
    }
    r.data = selectHeros;
    return r;
  }

  private List<Hero> backHerosCalc(Role role, HeroInheritCostRow costRow, int tid) {
    List<Hero> backHeros = new ArrayList<>();
    for (int i = 0; i < costRow.backHeroNum; ++i) {
      Hero hero = new Hero(role, tid);
      backHeros.add(hero);
    }
    return backHeros;
  }


  /**
   * 用狗粮替代原始的升星消耗
   */
  public static EcResult<Void> rebuildStarCost(Role role, MixRes reward, MixResItem dogItem) {
    EcResult<Void> r = new EcResult<>();
    StarUpItemRow row = role.getGame().table.item.starUp.get(dogItem.tid);
    if (row == null) {
      return r.fail("升星材料选择有误");
    }
    for (MixResItem item : reward.items) {
      // 代替指定星级阵营的英雄
      if (item.type == GDObj.Type.HERO_OF_STAR_CAMP && item.num >= dogItem.num) {
        int starReq = item.tid / 10;
        int campReq = item.tid % 10;
        if (starReq == row.star && (row.replaceCampValid((byte) campReq))) {
          item.num -= dogItem.num;
          return r.success();
        }
      } else if (item.type == GDObj.Type.HERO_OF_STAR && item.num >= dogItem.num) {
        if (item.tid == row.star) {
          item.num -= dogItem.num;
          return r.success();
        }
      } else if ((item.type == GDObj.Type.HERO_OF_STAR_NIUBI_CAMP ||
              item.type == GDObj.Type.HERO_OF_NIUBI_SOUL) && item.num >= dogItem.num) {
        if (item.tid == row.star &&
                (row.camps.stream().anyMatch(GDCamp.Type::isNiuBi) || row.camps.contains(GDCamp.Type.NULL))) {
          item.num -= dogItem.num;
          return r.success();
        }
      } else if (item.type == GDObj.Type.HERO_OF_NATURE_CAMP && item.num >= dogItem.num) {
        if (item.tid == row.star &&
                (row.camps.stream().anyMatch(GDCamp.Type::isNature) || row.camps.contains(GDCamp.Type.NULL))) {
          item.num -= dogItem.num;
          return r.success();
        }
      }
    }
    return r.fail("升星材料选择有误");
  }

  public GameResult<Void> fastOffHero(Role role, String heroUid) {
    GameResult<Void> positionOffRes = role.getGame().fight.position.batchOff(role, heroUid);
    if (!positionOffRes.ok()) {
      role.getGame().notice.message(role, positionOffRes);
      return positionOffRes;
    }
    if (role.getRes().worldTree.heroShare.heroInTree(heroUid)) {
      EcResult<Integer> worldtreeResult = worldTreeUnlock(role, heroUid);
      if (!worldtreeResult.ok()) {
        role.getGame().notice.message(role, worldtreeResult.message);
        return new GameResult<Void>().fail(worldtreeResult.message);
      }
    }
    role.getBase().updatePower();
    return new GameResult<>();
  }

  private EcResult<Integer> worldTreeUnlock(Role role, String heroUid) {
    EcResult<Integer> rr = role.getRes().worldTree.heroShare.delHeroShare(heroUid);
    if (rr.ok()) {
      Hero hero = role.getRes().worldTree.heroShare.update2ClientByOnlyId(heroUid);
      role.sendNow(new HeroShareDelResp(rr.data, heroUid, hero));
      role.sendNow(new HeroShareDataResp(role.getRes().worldTree.heroShare));
    }
    return rr;
  }

  /**
   * 英雄回退
   */
  public void back(Role role, String uid) {
    EcResult<Hero> r = findHeroResult(role, uid);
    if (!r.ok()) {
      role.getGame().notice.message(role, r.message);
      return;
    }
    Hero hero = r.data;
    GameResult<Void> offRes = fastOffHero(role, uid);
    if (!offRes.ok()) {
      return;
    }
    if (hero.yokeOn != null && MapUtils.isNotEmpty(hero.yokeOn.heroUidMap)) {
      GameResult<Byte> offYokeRes = hero.offAllYoke();
      if (!offYokeRes.ok()) {
        return;
      }
      role.sendNow(new YokeOffAllResp(uid));
    }
    int heroStar = hero.getTpl(role.toPlayBuildContext()).star;
    if (heroStar < 10) {
      role.getGame().notice.message(role, "该英雄不可回退");
      return;
    }
    if (hero.lock.islocked()) {
      role.getGame().notice.message(role, "该英雄已被锁定");
      return;
    }
    int backStar = 9;
    HeroBaseRow heroRow = hero.getTpl(role.toPlayBuildContext());
    HeroBaseRow backHeroRow = role.getGame().table.hero.base.getRow(heroRow.protoId, backStar);
    if (backHeroRow == null) {
      role.getGame().notice.message(role, "回退英雄不存在");
      return;
    }
    HeroBackConsumeRow consumeRow = role.getGame().table.heroBack.consume.getRow(heroStar);
    if (consumeRow == null) {
      role.getGame().notice.message(role, "回退方式有误");
      return;
    }
    EcResult<MixResItem> consumeRes = new MixRes(consumeRow.consumeStr).consume(role, null);
    if (!consumeRes.ok()) {
      role.getGame().notice.tipResItem(role, consumeRes.data);
      role.getGame().notice.message(role, consumeRes.message);
      return;
    }
    RewardShow show = backExe(role, hero, backStar);
    if (!show.isEmpty()) {
      role.getGame().notice.dialog(role, NoticeDialogResp.TYPE_REWARD, "恭喜获得", 0, show.tplRwd, show.insRwd);
    }
    role.sendNow(new HeroBackResp(uid, hero, show));
    role.getBag().hero.markHeroChanged();
  }

  /**
   * 执行回退（前置检测 扣费逻辑应该已经执行过）
   *
   * @param hero 英雄
   * @return show
   */
  public RewardShow backExe(Role role, Hero hero, int backStar) {
    return backExe(role, hero, backStar, 1, 0);
  }

  /**
   * 执行回退（前置检测 扣费逻辑应该已经执行过）
   *
   * @param hero     英雄
   * @param backStar 回退星级
   * @return show
   */
  public RewardShow backExe(Role role, Hero hero, int backStar, int backLevel, int backGrade) {
    HeroBaseRow heroRow = hero.getTpl(role.toPlayBuildContext());
    HeroBaseRow backHeroRow = role.getGame().table.hero.base.getRow(heroRow.protoId, backStar);

    // 用于记录展示的奖励
    RewardShow show = new RewardShow();
    MixRes backReward = new MixRes();
    // 需要返还的狗粮卡
    MixRes dogReward = calDogAndSelfReward(role, heroRow.camp, heroRow.protoId, heroRow.star, backStar);
    backReward.addList(dogReward);
    show.addTplRwd(dogReward);

    // 回退等级奖励
    if (hero.level > backLevel) {
      MixRes levelReward = calLevelReward(role.getGame(), hero.level, backLevel);
      backReward.addList(levelReward);
      show.addTplRwd(levelReward);
    }
    // 回退星级奖励
    MixRes starReward = calStarReward(role.getGame(), heroRow.star, backStar, heroRow.camp);
    backReward.addList(starReward);
    show.addTplRwd(starReward);

    // 回退阶数奖励
    if (hero.grade > backGrade) {
      MixRes gradeReward = calGradeReward(role.getGame(), hero.grade, backGrade);
      backReward.addList(gradeReward);
      show.addTplRwd(gradeReward);
    }
    MixRes starTaskReward = calcStarTaskReward(role, hero);
    if (!starTaskReward.isEmpty()) {
      backReward.addList(starTaskReward);
      show.addTplRwd(starTaskReward);
      hero.starTaskList.clear();
    }

    MixRes reward = new MixRes();
    hero.newTalentOn.list.forEach(item -> {
      reward.addList(new MixRes(item.getInheritTpl(role.toPlayBuildContext()).backStr));
    });
    backReward.addList(reward);
    hero.newTalentOn.list.clear();
    show.addTplRwd(reward);

    backReward.add(role, true, GDOperation.HERO_BACK);
    MergeMsgSender sender = new MergeMsgSender();
    show.merge(hero.offAll(role, sender));
    sender.send(role, true);
    // 修改星级和等级
    hero.tid = backHeroRow.getTemplateId();
    if (hero.level > backLevel) {
      hero.level = backLevel;
    }
    // 修复阶数
    if (hero.grade > backGrade) {
      hero.grade = backGrade;
    }
    HeroBaseRow newHeroRow = hero.getTpl(role.toPlayBuildContext());
    //先触发星级改变事件，可能会同时触发脱装备、灵器、徽章等
    publishEvent(role, new HeroLevelChangeEvent(role, hero, hero.level));
    publishEvent(role, new HeroStarChangeEvent(role, hero.uid, newHeroRow.star, heroRow.star, newHeroRow.camp));
    publishEvent(role, new HeroPowerChangeEvent(role, hero));
    role.getBase().updatePower();
    // 把本体英雄也塞进奖励里
    show.addInsRwd(GDObj.Type.HERO, hero.uid);
    HeroBaseUpdateResp.send(role, hero);
    role.getBag().hero.markHeroChanged();
    return show;
  }


  /**
   * 执行英雄献祭
   */
  public void sacrifice(Role role, Set<String> list) {
    // 验证传过来的英雄id list是否正确
    if (CollectionUtils.isEmpty(list)) {
      role.getGame().notice.message(role, "请选择需要放逐的英雄");
      return;
    }
    List<Hero> heros = new ArrayList<>();
    for (String uid : list) {
      Hero hero = role.getBag().hero.getItem(uid);
      if (hero == null) {
        role.getGame().notice.message(role, "英雄不存在");
        return;
      }
      if (hero.lock.islocked()) {
        role.getGame().notice.message(role, "该英雄已被锁定");
        return;
      }
      heros.add(hero);
    }
    // 消耗英雄
    EcResult<RewardShow> returnRes = calReturn(role, heros, sacrificeOpt());
    if (returnRes.ok() && returnRes.data != null && !returnRes.data.isEmpty()) {
      role.getGame().notice.dialog(role, NoticeDialogResp.TYPE_REWARD, "恭喜获得", 0, returnRes.data.tplRwd, returnRes.data.insRwd);
    }
    // 移除英雄
    MergeMsgSender sender = new MergeMsgSender();
    for (String costUid : list) {
      role.getBag().hero.removeItem(costUid, sender);
    }
    sender.send(role, true);
    if (!list.isEmpty()) {
      role.getHistory().action.heroSacrifice(list.size());
    }
    role.getBag().hero.markHeroChanged();
  }

  // 100% 返还材料英雄 升级 与 升阶 消耗
  public GameResult<RewardShow> forHeroList(Role role, CostSelect cs, Option op) {
    GameResult<RewardShow> r = new GameResult<>();
    if (cs == null) {
      return r.fail("英雄不存在");
    }
    List<Hero> costHeroList = new ArrayList<>();
    cs.options.forEach(
            (opt) -> {
              opt.uidList.forEach(
                      heroIdHex -> {
                        Hero hero = role.getBag().hero.getItem(heroIdHex);
                        if (hero != null && !hero.lock.islocked()) {
                          costHeroList.add(hero);
                        }
                      });
            });
    return calReturn(role, costHeroList, op);
  }

  public MixRes calDogAndSelfReward(Role role, byte camp, int protoId, int currentStar, int endStar) {
    MixRes reward = new MixRes();
    List<HeroBackDogRow> dogRows = role.getGame().table.heroBack.dog.getRewardRow(camp, currentStar, endStar);
    if (dogRows != null && !dogRows.isEmpty()) {
      for (HeroBackDogRow dogRow : dogRows) {
        reward.addList(new MixRes(dogRow.rewardStr));
        if (StringUtils.isNotBlank(dogRow.selfRewardStr)) {
          for (String selfStr : dogRow.selfRewardStr.split("#")) {
            String[] arr = selfStr.split(":");
            if (arr.length == 2) {
              int star = Integer.parseInt(arr[0]);
              int num = Integer.parseInt(arr[1]);
              HeroBaseRow selfRow = role.getGame().table.hero.base.getRow(protoId, star);
              if (selfRow != null) {
                reward.addItem(new MixResItem(GDObj.Type.HERO, selfRow.id, num));
              }
            }
          }
        }
      }
    }
    return reward;
  }

  private MixRes calSelfReward(Role role, int protoId, int currentStar, int backStar) {
    MixRes reward = new MixRes();
    for (int i = backStar; i < currentStar; i++) {
      HeroBaseRow row = role.getGame().table.hero.base.getRow(protoId, i);
      // 本体英雄
      reward.addList(calReward(role, row.cost0));
    }
    return reward;
  }

  private MixRes calReward(Role role, String cfg) {
    MixRes reward = new MixRes();
    MixRes consume = new MixRes(cfg);
    if (StringUtils.equals(cfg, "0") || StringUtils.isBlank(cfg)) {
      return reward;
    }
    for (MixResItem item : consume.items) {
      // 升星时指定原型的英雄
      if (item.type == GDObj.Type.HERO) {
        HeroBaseRow row = role.getGame().table.hero.base.get(item.tid);
        if (row != null) {
          reward.addList(calProtoRewardToFive(role, row).multiply((int) item.num));
        }
      }
    }
    return reward;
  }

  // 分解指定原型的英雄到五星
  private MixRes calProtoRewardToFive(Role role, HeroBaseRow row) {
    MixRes reward = new MixRes();

    // 返还本体 如果大于等于五星 则返还五星
    if (row.star >= 5) {
      HeroBaseRow fiveStarRow = role.getGame().table.hero.base.getRow(row.protoId, 5);
      if (fiveStarRow != null) {
        reward.addItem(new MixResItem(GDObj.Type.HERO, fiveStarRow.id, 1));
      }
    } else {
      reward.addItem(new MixResItem(GDObj.Type.HERO, row.id, 1));
    }

//        // 五星以上的英雄需要继续分解
    if (row.star > 5) {
      reward.addList(calSelfReward(role, row.protoId, row.star, 5).copy());
    }
    return reward;
  }

  public static GameResult<RewardShow> calReturn(Role role, List<Hero> list, Option opt) {
    GameResult<RewardShow> r = new GameResult<>();
    // 用于记录展示的奖励
    RewardShow show = new RewardShow();

    if (list == null) {
      return r.fail("英雄不存在");
    }
    try {
      // 装备返还
      MergeMsgSender sender = new MergeMsgSender();
      MixRes backReward = new MixRes();
      for (Hero hero : list) {
        // 等级奖励
        if (opt.level) {
          SacrificeLevelRow levelRow = role.getGame().table.sacrifice.level.get(hero.level);
          if (levelRow != null) {
            MixRes levelReward = new MixRes(levelRow.rewardStr);
            backReward.addList(levelReward);
            show.addTplRwd(levelReward);
          }
        }
        // 升阶奖励
        if (opt.grade) {
          SacrificeGradeRow gradeRow = role.getGame().table.sacrifice.grade.get(hero.grade);
          if (gradeRow != null) {
            MixRes gradeReward = new MixRes(gradeRow.rewardStr);
            backReward.addList(gradeReward);
            show.addTplRwd(gradeReward);
          }
        }
        // 星级奖励
        if (opt.star) {
          SacrificeStarRow starRow = role.getGame().table.sacrifice.star.get(hero.getTpl(role.toPlayBuildContext()).star);
          if (starRow != null) {
            MixRes starReward = new MixRes(starRow.rewardStr);
            if (starRow.vial > 0) {
              int itemId = GDHero.Vial.getItemByCamp(hero.getTpl(role.toPlayBuildContext()).camp);
              if (itemId > 0) {
                starReward.addItem(new MixResItem(GDObj.Type.PROP, itemId, starRow.vial));
              }
            }
            backReward.addList(starReward);
            show.addTplRwd(starReward);
          }
          MixRes starTaskReward = calcStarTaskReward(role, hero);
          if (!starTaskReward.isEmpty()) {
            backReward.addList(starTaskReward);
            show.addTplRwd(starTaskReward);
          }
        }
        if (opt.heroTalent) {

          //英雄天赋
          MixRes reward = new MixRes();
          hero.newTalentOn.list.forEach(item -> {
            reward.addList(new MixRes(item.getInheritTpl(role.toPlayBuildContext()).backStr));
          });
          hero.newTalentOn.list.clear();
          backReward.addList(reward);
          show.addTplRwd(reward);
        }
        show.merge(hero.offAll(role, sender));
      }
      backReward.add(role, true, opt.operation, sender);
      sender.send(role, true);
    } catch (Exception e) {
      //nothing
      log.error("返还材料异常, ", e);
    }
    r.data = show;
    return r;
  }

  private static MixRes calcStarTaskReward(Role role, Hero hero) {
    // 高阶星级奖励
    MixRes starTaskReward = new MixRes();
    HeroBaseRow baseRow = hero.getTpl(role.getGame());
    if (baseRow != null) {
      int star = baseRow.star;
      for (int i = 5; i < star; ++i) {
        baseRow = role.getGame().table.hero.base.getRow(baseRow.protoId, i);
        if (!baseRow.taskMap.isEmpty()) {
          starTaskReward.addList(calcTasksBackReward(role, baseRow.taskMap.values()));
        }
      }
      if (!hero.starTaskList.isEmpty()) {
        starTaskReward.addList(calcTasksBackReward(role, hero.starTaskList));
      }
    }
    return starTaskReward;
  }

  private static MixRes calcCurStarTaskReward(Role role, Hero hero) {
    // 高阶星级奖励
    MixRes starTaskReward = new MixRes();
    if (!hero.starTaskList.isEmpty()) {
      starTaskReward.addList(calcTasksBackReward(role, hero.starTaskList));
    }
    return starTaskReward;
  }

  private static MixRes calcTasksBackReward(Role role, Collection<Integer> collection) {
    MixRes backReward = new MixRes();
    for (int starTaskId : collection) {
      HeroBackStarTaskRow heroStarTaskRow = role.getGame().table.heroBack.starTask.get(starTaskId);
      if (heroStarTaskRow == null || StringUtils.isBlank(heroStarTaskRow.consumeStr)) {
        continue;
      }
      backReward.addList(new MixRes(heroStarTaskRow.consumeStr));
    }
    return backReward;
  }

  /**
   * 计算阶数回退的奖励
   *
   * @param currentGrade 当前阶数
   * @param backGrade    回退的阶数
   * @return 奖励
   */
  private MixRes calGradeReward(GameBody game, int currentGrade, int backGrade) {
    MixRes reward = new MixRes();
    if (currentGrade == backGrade) {
      return reward;
    }
    List<HeroBackGradeRow> rows = game.table.heroBack.grade.getRows(currentGrade, backGrade);
    for (HeroBackGradeRow row : rows) {
      reward.addList(new MixRes(row.rewardStr));
    }
    return reward;
  }

  /**
   * 计算等级回退的奖励
   *
   * @param currentLevel 当前等级
   * @param backLevel    回退的等级
   * @return reward
   */
  private MixRes calLevelReward(GameBody game, int currentLevel, int backLevel) {
    MixRes reward = new MixRes();
    if (currentLevel == backLevel) {
      return reward;
    }
    HeroBackLevelRow oldRow = game.table.heroBack.level.get(currentLevel);
    HeroBackLevelRow newRow = game.table.heroBack.level.get(backLevel);
    if (oldRow == null || newRow == null) {
      return reward;
    }
    return new MixRes(oldRow.rewardStr).minusList(new MixRes(newRow.rewardStr));
  }

  private MixRes calStarReward(GameBody game, int currentStar, int backStar, byte camp) {
    MixRes reward = new MixRes();
    if (currentStar == backStar) {
      return reward;
    }
    List<HeroBackStarRow> rows = game.table.heroBack.star.getRows(currentStar, backStar);
    for (HeroBackStarRow row : rows) {
      reward.addList(new MixRes(row.rewardStr));
      if (row.vial > 0) {
        int vialTid = GDHero.Vial.getItemByCamp(camp);
        if (vialTid > 0) {
          reward.addItem(new MixResItem(GDObj.Type.PROP, vialTid, row.vial));
        }
      }
    }
    return reward;
  }

  public static Option sacrificeOpt() {
    Option opt = new Option();
    opt.operation = GDOperation.HERO_SACRIFICE;
    return opt;
  }

  public static Option starUpOpt() {
    Option opt = new Option();
    opt.star = false;
    opt.operation = GDOperation.HERO_STAR_UP;
    return opt;
  }

  private static Option rebornOpt() {
    Option opt = new Option();
    opt.star = false;
    opt.operation = GDOperation.HERO_REBORN;
    opt.talent = false;
    return opt;
  }

  private static Option inheritOpt() {
    Option opt = new Option();
    opt.star = false;
    opt.operation = GDOperation.HERO_INHERIT;
    return opt;
  }

  public static Option convertHeroOpt() {
    Option opt = new Option();
    opt.star = false;
    opt.operation = GDOperation.HERO_CONVERT;
    return opt;
  }

  /**
   * 英雄升星
   *
   * @param role
   * @param uid
   * @param taskId
   */
  public void starUp(Role role, String uid, Map<Integer, CostSelect> csbbMap, int taskId) {
    RewardShow rewardShow = new RewardShow();
    List<StarUpResVO> res = new ArrayList<>();
    GameResult<Hero> r = findHeroResult(role, uid);
    if (!r.ok()) {
      role.getGame().notice.message(role, r.message);
    }
    Hero old = r.data;
    int tidOld = old.tid;
    HeroBaseRow oldTpl = old.getTpl(role.toPlayBuildContext());
    if (oldTpl.nextStarId == GD.INT_0) {
      role.getGame().notice.message(role, "该英雄当前已达星级上限");
      return;
    }
    if (oldTpl.worldLevelRequire > role.getRes().worldTree.heroShare.shareLvl) {
      role.getGame().notice.message(role, String.format("共鸣等级达到%d级，可以继续升星", oldTpl.worldLevelRequire));
      return;
    }
    if (old.getTpl(role.toPlayBuildContext()).upType == GDHero.UpType.NORMAL) {
      r = normalStarUp(role, old, csbbMap, rewardShow);
      if (!r.ok()) {
        role.getGame().notice.message(role, r.message);
        return;
      }
      StarUpResVO resVo = new StarUpResVO();
      resVo.uid = uid;
      resVo.tidOld = tidOld;
      resVo.tidNew = r.data.tid;
      resVo.taskId = taskId;
      res.add(resVo);
      HeroBaseUpdateResp.send(role, r.data);

      if (old.lock.heroReplace) {
        old.lock.releaseReplaceLock(role);
        role.getRes().callPO.release(null);
      }

      if (!res.isEmpty()) {
        role.getBase().updatePower();
        role.sendNow(new HeroBatchStarUpResp(res, rewardShow));
      }
      role.getBag().hero.markHeroChanged();
    } else {
      r = role.getFarm().trainHeroFight.taskStarUp(role, old, csbbMap, rewardShow, taskId);
      if (!r.ok()) {
        role.getGame().notice.message(role, r.message);
        return;
      }
    }
  }


  private static class Option {
    /**
     * 返回 等级奖励
     */
    public boolean level = true;
    /**
     * 返回 升阶奖励
     */
    public boolean grade = true;
    /**
     * 返回 星级奖励
     */
    public boolean star = true;
    /**
     * 返回 天赋奖励
     */
    public boolean talent = true;

    /**
     * 返回 英雄天赋奖励
     */
    public boolean heroTalent = true;

    public int operation;

    public Option() {

    }

    public Option(int operation) {
      this.operation = operation;
    }
  }
}
